﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace Red.GameData.Raw
{
    partial class ReflectionData
    {
        /// <summary>
        /// Load reflection data from given directory
        /// Loads all the projects
        /// </summary>
        /// <param name="log">General error/proggres reporting interface</param>
        /// <param name="rootPath">Root path for the reflection data</param>
        /// <returns>Created reflection data or null on errors</returns>
        public static ReflectionData Load(IGenericFileErrorReporter log, string rootPath)
        {
            ReflectionData ret = new ReflectionData(rootPath);

            // load files
            int numLoadingErrors = 0;
            try
            {
                if (Directory.Exists(rootPath))
                {
                    string[] files = Directory.GetFiles(rootPath, "*.redschema", SearchOption.AllDirectories);

                    int fileIndex = 0;
                    foreach (string filePath in files)
                    {
                        // report file progress
                        log.StartFile(filePath);
                        log.TotalProgress(fileIndex++, files.Length);

                        // load XML
                        XmlDocument doc = new XmlDocument();
                        try
                        {
                            // load the XML into the XmlDocument
                            // TODO: this is soooo lame :( sorry
                            doc.Load(filePath);

                            // process file nodes
                            foreach (XmlNode rootNodes in doc.ChildNodes)
                            {
                                foreach (XmlNode node in rootNodes.ChildNodes)
                                {
                                    if (node.Name == "class")
                                    {
                                        if (!ReflectionClass.LoadFromNode(ret, log, node))
                                            numLoadingErrors += 1;
                                    }
                                    else if (node.Name == "enum")
                                    {
                                        if (!ReflectionEnum.LoadFromNode(ret, log, node))
                                            numLoadingErrors += 1;
                                    }
                                }
                            }
                        }
                        catch (XmlException e)
                        {
                            log.Error("XML loading exception:\r\n" + e.ToString());
                            numLoadingErrors += 1;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                log.Error( "Fatal loading exception:\r\n" + e.ToString());
                return null;
            }

            // there were loading errors..
            if (numLoadingErrors > 0)
            {
                ret = null;
                log.Error(String.Format("Found {0} errors during loading phase. The type definitions XMLs are corrupted or not up to date.", numLoadingErrors));
                return null;
            }

            // resolve types
            int classIndex = 0;
            int numResolveErrors = 0;
            foreach (var classDef in ret.AllClasses.Values)
            {
                log.StartFile("Resolving " + classDef.Name);
                log.TotalProgress(classIndex++, ret.AllClasses.Count);

                foreach (var propDef in classDef.ClassProperties)
                {
                    if (!propDef.ResolveType(log))
                    {
                        numResolveErrors += 1;
                    }
                }
            }

            // errors were found during type resolve
            if (numResolveErrors > 0)
            {
                ret = null;
                log.Error(String.Format("Found {0} errors during type resolve phase. The type definitions XMLs are corrupted or not up to date.", numResolveErrors));
                return null;
            }

            // return loaded type registry
            return ret;
        }

    }
}
